home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / lang / mc302 / dosutil / mterm.c < prev    next >
C/C++ Source or Header  |  1994-03-18  |  17KB  |  679 lines

  1. /*
  2.  * MICRO-Terminal:
  3.  *
  4.  * This is a very simple communications program, which provides
  5.  * a subset ANSI (VT100) terminal emulation, and basic XMODEM
  6.  * (with checksum) file transfer.
  7.  *
  8.  * If HOTKEYS are specified on the command line, MTERM will install
  9.  * itself as a TSR (Ram-Resident) program, which can be invoked at
  10.  * any time by pressing the HOTKEYS. Available HOTKEYS are:
  11.  *        L - Left SHIFT
  12.  *        R - Right SHIFT
  13.  *        A - ALT
  14.  *        C - CONTROL
  15.  *        S - SysRq (Caution: some systems may not like this one)
  16.  *
  17.  *        EG: mterm LR    (Install with LEFT+RIGHT SHIFT for hotkeys)
  18.  *
  19.  * This demonstrates the use of the MICRO-C video interface
  20.  * and communications library functions for the IBM/PC, as well
  21.  * as the "SAVE_VIDEO", "RESTORE_VIDEO" and "TSR" functions.
  22.  *
  23.  * Copyright 1990-1994 Dave Dunfield
  24.  * All rights reserved.
  25.  *
  26.  * Permission granted for personal (non-commercial) use only.
  27.  *
  28.  * Compile command: cc mterm -fop
  29.  */
  30. #include <stdio.h>            /* Standard I/O definitions */
  31. #include <comm.h>            /* Comm     I/O definitions */
  32. #include <video.h>            /* Video    I/O definitions */
  33. #include <file.h>            /* File        I/O definitions */
  34. #include <tsr.h>            /* Tsr function definitions */
  35.  
  36. /* Screen output positions */
  37. #define    SETROW        3        /* Screen row for settings display */
  38. #define MSGROW        20        /* Screen row for messages */
  39. #define    MENROW        7        /* Screen row for menu items */
  40. #define    MAICOL        0        /* Screen column for main menu */
  41. #define    SUBCOL        20        /* Screen column for sub menu */
  42. #define    FILCOL        5        /* Screen column for file prompt */
  43. #define    FILSIZ        50        /* Maximum size of file name */
  44.  
  45. /* XMODEM parameters */
  46. #define BLOCK_SIZE    128        /* size of transmit blocks */
  47. #define RETRYS        10        /* maximum number of retrys */
  48. #define    SOH_TIMEOUT    5        /* How long to wait for start of packet */
  49. #define    RX_TIMEOUT    2        /* How long in wait for chars in packet */
  50. #define    ACK_TIMEOUT    5        /* How long to wait for acknowlege */
  51.  
  52. /* Line control codes */
  53. #define SOH            0x01    /* start of header */
  54. #define ACK            0x06    /* Acknowledge */
  55. #define NAK            0x15    /* Negative acknowledge */
  56. #define CAN            0x18    /* Cancel */
  57. #define EOT            0x04    /* end of text */
  58.  
  59. /* Menu text tables (Used by 'vmenu') */
  60.     char *main_menu[] = {
  61.         "Terminal Emulation",
  62.         "XMODEM Download",
  63.         "XMODEM Upload",
  64.         "Serial port config",
  65.         "Exit to DOS",
  66.         0 };
  67.  
  68.     char *setup_menu[] = {
  69.         "Comm port",
  70.         "Baudrate",
  71.         "Data bits",
  72.         "Parity",
  73.         "Stop bits",
  74.         "Xon/Xoff",
  75.         0 };
  76.  
  77. /* Uart configuration data tables */
  78. unsigned baudvalue[] =
  79.     { _110,  _300,  _1200,  _2400,  _4800,  _9600,  _19200,  _38400 };
  80. char *baudtext[] =
  81.     { "110", "300", "1200", "2400", "4800", "9600", "19200", "38400", 0 };
  82. char *databits[] = { "Five", "Six", "Seven", "Eight", 0 };
  83. char *parity[] = { "Odd", "Even", "Mark", "Space", "None", 0 };
  84. char *onetwo[] = { "One", "Two", 0 };
  85. char *onefour[] = { "One", "Two", "Three", "Four", 0 };
  86. char *flowctrl[] = { "Disabled", "Enabled", 0 };
  87.  
  88. /* Communications configuration parameters */
  89. int comm = 0, baud = 5, data = 3, par = 4, stop = 0, flow = 1;
  90.  
  91. /* Misc global variables */
  92. setup_selection = 0, transfer_selection = 0;
  93. char dfile[FILSIZ+1] = "", ufile[FILSIZ+1] = "";
  94.  
  95. /* Saved video screens, attributes & cursor position */
  96. char sav_buffer[(25*80)*2], sav_attr;
  97. int sav_xy;
  98. char video_save_area[SCR_BUF];
  99.  
  100. /*
  101.  * Main terminal program menu
  102.  */
  103. tty_main()
  104. {
  105.     int i;
  106.  
  107.     i = 0;            /* Default to top of menu */
  108.     save_video(video_save_area);
  109.  
  110. redraw:
  111.     draw_title();
  112.     vdraw_box(0, SETROW, 79, 2);
  113.     show_settings();
  114.  
  115.     for(;;) {
  116.         message("Select function and press ENTER");
  117.         if(vmenu(MAICOL, MENROW, main_menu, 0, &i))
  118.             continue;
  119.         switch(i) {
  120.             case 0 :        /* Terminal Emulation */
  121.                 if(!open_comm(flow))
  122.                     break;
  123.                 vcursor_line();
  124.                 restore_screen();
  125.                 ansi_term();
  126.                 save_screen();
  127.                 vcursor_off();
  128.                 goto redraw;
  129.             case 1 :        /* Download a file */
  130.                 if(open_comm(0))
  131.                     download(dfile);
  132.                 break;
  133.             case 2 :        /* Upload a file */
  134.                 if(open_comm(0))
  135.                     upload(dfile);
  136.                 break;
  137.             case 3 :        /* Setup serial port */
  138.                 setup();
  139.                 break;
  140.             case 4 :        /* Exit to DOS */
  141.                 Cclose();
  142.                 restore_video(video_save_area);
  143.                 return; } }
  144. }
  145.  
  146. /*
  147.  * Open a file for read or write (with overwrite prompt)
  148.  */
  149. HANDLE openf(fname, rw)
  150.     char *fname, rw;
  151. {
  152.     char c, omsg[80], *mode;
  153.     HANDLE fh;
  154.  
  155.     mode = "read";
  156.     fh = open(fname, F_READ);        /* First try and read the file */
  157.     if(rw) {                    /* If writing the file */
  158.         mode = "write";
  159.         if(fh) {
  160.             close(fh);
  161.             sprintf(omsg, "Overwrite existing %s (Y/N) ?", fname);
  162.             message(omsg);
  163.             do {
  164.                 c = toupper(vgetc());
  165.                 if((c == 0x1B) || (c == 'N'))
  166.                     return 0; }
  167.             while(c != 'Y'); }
  168.         fh = open(fname, F_WRITE); }
  169.     if(!fh) {
  170.         sprintf(omsg,"Cannot %s %s (Press ENTER)", mode, fname);
  171.         message(omsg);
  172.         while(vgetc() != '\n'); }
  173.     return fh;
  174. }
  175.  
  176. /*
  177.  * Open comm port with correct settings
  178.  */
  179. open_comm(flow)
  180.     char flow;
  181. {
  182.     int mode;
  183.  
  184.     /* Calculate the communications parameter value */
  185.     mode =    ((par << 4) & 0x30) |    /* parity type */
  186.             (data & 0x03) |            /* # data bits */
  187.             ((stop << 2) & 0x04) |    /* # stop bits */
  188.             ((par < 4) << 3);        /* parity enable */
  189.  
  190.     /* Open the communications port */
  191.     if(Copen(comm+1, baudvalue[baud], mode, SET_DTR|SET_RTS|OUTPUT_2)) {
  192.         message("Cannot open COM port (Press ENTER)");
  193.         while(vgetc() != '\n');
  194.         return 0; }
  195.  
  196.     /* Remove transparency if XON/XOFF flow control */
  197.     disable();
  198.     Cflags = (flow) ? Cflags & ~TRANSPARENT : Cflags | TRANSPARENT;
  199.     enable();
  200.  
  201.     return -1;
  202. }
  203.  
  204. /*
  205.  * Draw the title  header
  206.  */
  207. draw_title()
  208. {
  209.     vopen();
  210.     V_ATTR = REVERSE;
  211.     vdraw_box(0, 0, 79, 2);
  212.     vgotoxy(1, 1);
  213.     vputf("", 26);
  214.     vputf("MICRO-Terminal Version 1.3", 52);
  215.     V_ATTR = NORMAL;
  216.     vcursor_off();
  217. }
  218.  
  219. /*
  220.  * Draw the file transfer information box
  221.  */
  222. info_box(mode, filename)
  223.     char *mode, *filename;
  224. {
  225.     vdraw_box(SUBCOL, MENROW+1, 50, 8);
  226.     vgotoxy(SUBCOL+2, MENROW+3);
  227.     vprintf("%-19s: %s", mode, filename);
  228.     vgotoxy(SUBCOL+2, MENROW+5);
  229.     vputs("Blocks transferred : 0");
  230.     vgotoxy(SUBCOL+2, MENROW+7);
  231.     vputs("Transfer status    : ");
  232.     message("File transfer in progress (ESCAPE to abort)");
  233. }
  234.  
  235. /*
  236.  * Update the transfer status field
  237.  */
  238. transfer_status(text)
  239.     char *text
  240. {
  241.     vgotoxy(SUBCOL+23, MENROW+7);
  242.     vputf(text,10);
  243. }
  244.  
  245. /*
  246.  * Show the current COM port settings
  247.  */
  248. show_settings()
  249. {
  250.     vgotoxy(18, SETROW+1);
  251.     vprintf("COM%u: %5s,%2d,%5s,%2d  Xon/Xoff %-8s",
  252.         comm+1, baudtext[baud], data+5, parity[par], stop+1, flowctrl[flow]);
  253. }
  254.  
  255. /*
  256.  * Display a message
  257.  */
  258. message(ptr)
  259.     char *ptr;
  260. {
  261.     vgotoxy(0, MSGROW);
  262.     vcleos();
  263.     vmessage(38 - strlen(ptr)/2, MSGROW, ptr);
  264. }
  265.  
  266. /*
  267.  * Save the MICRO-TERMINAL video screen.
  268.  */
  269. save_screen()
  270. {
  271.     sav_xy = V_XY;
  272.     sav_attr = V_ATTR;
  273.     copy_seg(get_ds(), sav_buffer, V_BASE, 0, (25*80)*2);
  274. }
  275.  
  276. /*
  277.  * Restore the MICRO-TERMINAL video screen
  278.  */
  279. restore_screen()
  280. {
  281.     copy_seg(V_BASE, 0, get_ds(), sav_buffer, (25*80)*2);
  282.     V_ATTR = sav_attr;
  283.     V_XY = sav_xy;
  284.     vupdatexy();
  285. }
  286.  
  287. /*
  288.  * Comm port setup menu handler
  289.  */
  290. setup()
  291. {
  292.     message("Select setting (ESCAPE to cancel)");
  293.     for(;;) {
  294.         show_settings();
  295.         if(vmenu(SUBCOL, MENROW+1, setup_menu, 0, &setup_selection))
  296.             return;
  297.         switch(setup_selection) {
  298.             case 0 :    /* Comm port */
  299.                 vmenu(SUBCOL+11,MENROW+2,onefour,-1,&comm);
  300.                 break;
  301.             case 1 :    /* baudrate */
  302.                 vmenu(SUBCOL+11,MENROW+2,baudtext,-1,&baud);
  303.                 break;
  304.             case 2 :    /* Data bits */
  305.                 vmenu(SUBCOL+11,MENROW+2,databits,-1,&data);
  306.                 break;
  307.             case 3 :    /* Parity */
  308.                 vmenu(SUBCOL+11,MENROW+2,parity,-1,&par);
  309.                 break;
  310.             case 4 :    /* Stop bits */
  311.                 vmenu(SUBCOL+11,MENROW+2,onetwo,-1,&stop);
  312.                 break;
  313.             case 5 :    /* Flow control */
  314.                 vmenu(SUBCOL+11,MENROW+2,flowctrl,-1,&flow); } }
  315. }
  316.  
  317. /*
  318.  * Terminal mode using ANSI (VT100) emulation
  319.  */
  320. ansi_term()
  321. {
  322.     char c, xy_flag, *ptr;
  323.     unsigned x, y, state, value, parm, parms[5];
  324.  
  325.     /* ANSI (VT100) Function key translation table */
  326.     static char *ansi_keys[] = {
  327.         "\x1B[A", "\x1B[B", "\x1B[D", "\x1B[C",    /* Arrow keys */
  328.         "\x1BOR", "\x1BOS", "\x1BOP", "\x1BOQ",    /* PgUp, Pgdn, Home, End */
  329.         "\x1BOM", "\x1BOm", "\x1BOp",            /* Keypad '+','-' Insert */
  330.         "\x7F",   "\x08",                        /* Delete & Backspace */
  331.         "\x1BOq", "\x1BOr", "\x1BOs", "\x1BOt",    /* F1, F2, F3 & F4 */
  332.         "\x1BOu", "\x1BOv", "\x1BOw", "\x1BOx",    /* F5, F6, F7 & F8 */
  333.         "\x1BOy", "\x1BOz",                        /* F9 & F10 */
  334.         "\x1BOl", "\x1BOn", 0, 0 };        /* Control: Pgup, Pgdn, Home, End */
  335.  
  336.     xy_flag = -1;        /* Force initial cursor update */
  337.     state = 0;            /* Not receiving a control sequence */
  338.     for(;;) {
  339.         /* Process any input from the comm port */
  340.         if((c = Ctestc()) != -1) {
  341.             xy_flag = -1;
  342.             if(c == 0x1B) {                /* Begin escape sequence */
  343.                 state = 1;
  344.                 parms[0] = parms[1] = value = parm = 0; }
  345.             else switch(state) {
  346.                 case 1 :                /* Escape already received */
  347.                     if(c == '[') {
  348.                         state = 2;
  349.                         break; }
  350.                     state = 0;
  351.                 case 0 :                /* No special processing */
  352.                     vputc(c);
  353.                     break;
  354.                 case 2 :                /* Waiting for numeric parms */
  355.                     if(isdigit(c)) {
  356.                         value = (value * 10) + (c - '0');
  357.                         break; }
  358.                     parms[parm++] = value;    /* More to come */
  359.                     if(c == ';') {
  360.                         value = 0;
  361.                         break; }
  362.                     state = 0;
  363.                     switch(c) {
  364.                         case 'H' :        /* Cursor position (1) */
  365.                         case 'f' :        /* Cursor position (2) */
  366.                             if(y = parms[0])
  367.                                 --y;
  368.                             if(x = parms[1])
  369.                                 --x;
  370.                             vgotoxy(x, y);
  371.                             break;
  372.                         case 'J' :        /* Erase in display */
  373.                             x = V_XY;
  374.                             value ? vclscr() : vcleos();
  375.                             V_XY = x;
  376.                             break;
  377.                         case 'K' :        /* Erase in line */
  378.                             x = V_XY;
  379.                             if(value)
  380.                                 V_XY &= 0xff00;
  381.                             vcleol();
  382.                             V_XY = x;
  383.                             break;
  384.                         case 'm' :        /* Select attributes */
  385.                             x = 0;
  386.                             do {
  387.                                 V_ATTR  = (y = parms[x]) ? (y == 4) ?
  388.                                     UNDERLINE : REVERSE : NORMAL; }
  389.                             while(++x < parm); } } }
  390.         else if(xy_flag) {                /* Cursor has moved */
  391.             vupdatexy();
  392.             xy_flag = 0; }
  393.  
  394.         /* Process any input from the keyboard */
  395.         if(c = vtstc()) {
  396.             if(c & 0x80) {                /* Special function key */
  397.                 if(!(ptr = ansi_keys[c & 0x7f]))
  398.                     return;
  399.                 while(*ptr)
  400.                     Cputc(*ptr++); }
  401.             else
  402.                 Cputc((c == '\n') ? '\r' : c); } }
  403. }
  404.  
  405. /*
  406.  * Receive a file in XMODEM protocol
  407.  */
  408. download()
  409. {
  410.     char rbuffer[BLOCK_SIZE], *error_text, *old_error, ochr;
  411.     int r, rx_block_num, error_count;
  412.     HANDLE fh;
  413.  
  414.     if(vgets(FILCOL,MSGROW,"Write to file? ",dfile,FILSIZ) || !*dfile)
  415.         return;
  416.  
  417.     if(!(fh = openf(dfile, -1)))
  418.         return;
  419.  
  420.     info_box("Download to file", dfile);
  421.  
  422.     error_text = old_error = rx_block_num = 1;
  423.     error_count = RETRYS;
  424.     do {
  425.         if(vtstc() == 0x1b) {        /* Console escape */
  426.             ochr = CAN;
  427.             error_text = "CANCELED";
  428.             error_count = 0; }
  429.         else if((r = get_record(rbuffer)) == (rx_block_num & 255)) {
  430.             error_count = RETRYS;
  431.             write(rbuffer, BLOCK_SIZE, fh);
  432.             vgotoxy(SUBCOL+23, MENROW+5);
  433.             vprintf("%u", rx_block_num++);
  434.             error_text = "RX PACKET";
  435.             ochr = ACK; }
  436.         else {
  437.             switch(r) {
  438.                 case -1 :        /* Timeout */
  439.                     error_text = "TIMEOUT";
  440.                     ochr = NAK;
  441.                     break;
  442.                 case -2 :        /* Bad block */
  443.                     error_text = "BAD BLOCK#";
  444.                     while(Cgett(RX_TIMEOUT) != -1);
  445.                     ochr = NAK;
  446.                     break;
  447.                 case -3 :        /* Bad checksum */
  448.                     error_text = "BAD CHKSUM";
  449.                     ochr = NAK;
  450.                     break;
  451.                 case -4 :        /* End of file */
  452.                     error_text = "DONE";
  453.                     ochr = ACK;
  454.                     break;
  455.                 case -5 :        /* Cancel */
  456.                     error_text = "ABORTED";
  457.                     ochr = ACK;
  458.                     break;
  459.                 default:        /* Block out of sequence */
  460.                     error_text = "WRONG BLK";
  461.                     ochr = NAK; }
  462.                 --error_count; }
  463.             Cputc(ochr);
  464.             /* Update status message */
  465.             if(error_text != old_error)
  466.                 transfer_status(old_error = error_text); }
  467.         while((r > -3) && error_count);
  468.  
  469.     message("Download ended (Press ENTER)");
  470.     close(fh);
  471.     while(vgetc() != '\n');
  472.     vclear_box(SUBCOL, MENROW+1, 50, 8);
  473. }
  474.  
  475. /*
  476.  * Read a record in the XMODEM protocol, return the block number
  477.  * (0-255) if successful, or one of the following return codes:
  478.  *    -1 = Timeout
  479.  *    -2 = Bad block number
  480.  *    -3 = Bad block checksum
  481.  *    -4 = End of file
  482.  *    -5 = Canceled by remote
  483.  */
  484. get_record(rbuffer)
  485.     char rbuffer[];
  486. {
  487.     int c, i, block_num, check_sum;
  488.  
  489.     check_sum = 0;
  490.     i = -2;
  491.     switch(Cgett(SOH_TIMEOUT)) {
  492.         case SOH :        /* Receive packet */
  493.             for(;;) {
  494.                 if((c = Cgett(RX_TIMEOUT)) == -1)    /* receive timeout */
  495.                     break;
  496.                 if(i == -2)                            /* first block number */
  497.                     block_num = c;
  498.                 else if(i == -1) {                    /* second block number */
  499.                     if((255 & ~c) != block_num)
  500.                         return -2; }
  501.                 else if(i == BLOCK_SIZE)            /* checksum at end */
  502.                     return (check_sum & 0xff) == c ? block_num : -3;
  503.                 else                                /* data character */
  504.                     check_sum += (rbuffer[i] = c);
  505.                 ++i; }
  506.         case -1 :        /* timeout on waiting for packet */
  507.             return -1;
  508.         case EOT :        /* end of file encountered */
  509.             return -4;
  510.         case CAN :        /* cancel protocol */
  511.             return -5; }
  512. }
  513.  
  514. /*
  515.  * Transmit a file in XMODEM protocol
  516.  */
  517. upload()
  518. {
  519.     int i, c, tx_block_num, error_count;
  520.     char buffer[BLOCK_SIZE], *error_text, *old_error;
  521.     HANDLE fh;
  522.  
  523.     if(vgets(FILCOL,MSGROW,"Read from file? ",ufile,FILSIZ) || !*ufile)
  524.         return;
  525.  
  526.     if(!(fh = openf(ufile, 0)))
  527.         return;
  528.  
  529.     info_box("Upload from file", ufile);
  530.  
  531.     tx_block_num = old_error = error_text = 1;
  532.     error_count = RETRYS;
  533.  
  534.     /* Transmit the file data */
  535.     while(i = read(buffer, BLOCK_SIZE, fh)) {
  536.         while(i < 128)
  537.                 buffer[i++] = -1;
  538.         error_text = "TX PACKET";
  539.         while(i = send_record(buffer, tx_block_num)) {
  540.             switch(i) {
  541.                 case -1 :
  542.                     error_text = "TIMEOUT";
  543.                     break;
  544.                 case -3 :
  545.                     error_text = "RECV NAK";
  546.                     break;
  547.                 case -5 :
  548.                     error_text = "ABORTED"; }
  549.                 transfer_status(old_error = error_text);
  550.                 if((i < -3) || !error_count--)
  551.                     break; }
  552.         if(vtstc() == 0x1b) {        /* Console escape */
  553.             i = -5;
  554.             error_text = "CANCELED"; }
  555.         if(i) {                        /* Error exit */
  556.             Cputc(CAN);
  557.             break; }
  558.         error_count = RETRYS;
  559.         vgotoxy(SUBCOL+23, MENROW+5);
  560.         vprintf("%u", tx_block_num++);
  561.         if(error_text != old_error)
  562.             transfer_status(old_error = error_text); }
  563.  
  564.     /* Send the end of file indicator */
  565.     error_count = RETRYS;
  566.     if(!i) for(;;) {
  567.         Cputc(EOT);
  568.         if((c = Cgett(ACK_TIMEOUT)) == ACK) {
  569.             error_text = "DONE";
  570.             break; }
  571.         if(c == CAN) {
  572.             error_text = "ABORTED";
  573.             break; }
  574.         if(((c == -1) || (c == NAK)) && !error_count--) {
  575.             error_text = "TIMEOUT";
  576.             break; } }
  577.  
  578.     transfer_status(error_text);
  579.     message("Upload ended (Press ENTER)");
  580.     close(fh);
  581.     while(vgetc() != '\n');
  582.     vclear_box(SUBCOL, MENROW+1, 50, 8);
  583. }
  584.  
  585. /*
  586.  * Send an record in XMODEM protocol, return 0 if successful
  587.  * Otherwise, return one of the following:
  588.  *    -1 = Timeout
  589.  *    -2 = Bad block number    (N/A)
  590.  *    -3 = Bad block            (NAK RECEIVED)
  591.  *    -4 = End of file        (N/A)
  592.  *    -5 = Canceled by remote
  593.  */
  594. send_record(buffer, block_num)
  595.     char *buffer;
  596.     int block_num;
  597. {
  598.     int i, check_sum;
  599.     char *ptr;
  600.  
  601.     check_sum = 0;
  602.     ptr = buffer;
  603.     while(Ctestc() != -1);        /* purge any received data */
  604.     Cputc(SOH);
  605.     Cputc(block_num);
  606.     Cputc(~block_num);
  607.     for(i=0; i < BLOCK_SIZE; ++i) {
  608.         Cputc(*buffer);
  609.         check_sum += *buffer++; }
  610.     Cputc(check_sum);
  611.  
  612.     for(;;) switch(Cgett(ACK_TIMEOUT)) {
  613.         case ACK :            /* Packet received ok */
  614.             return 0;
  615.         case NAK :            /* Rejected */
  616.             return -3;
  617.         case CAN :            /* Remote cancel */
  618.             return -5;
  619.         case -1 :            /* Timeout */
  620.             return -1; }
  621. }
  622.  
  623. /*
  624.  * Wait for a character from the modem (with timeout)
  625.  */
  626. Cgett(timeout)
  627.     unsigned timeout;
  628. {
  629.     int h, m, s, old_s;
  630.  
  631.     if((h = Ctestc()) != -1)    /* Very fast if characters buffered */
  632.         return h;
  633.  
  634.     get_time(&h, &m, &old_s);
  635.     do {
  636.         do {
  637.             if((h = Ctestc()) != -1)
  638.                 return h;
  639.             get_time(&h,&m,&s); }
  640.         while(s == old_s);
  641.         old_s = s; }
  642.     while(--timeout);
  643.     return -1;
  644. }
  645.  
  646. /*
  647.  * Main program, either TSR or execute main tty program menu
  648.  */
  649. main(argc, argv)
  650.     int argc;
  651.     int *argv[];
  652. {
  653.     int hot_keys;
  654.     char *ptr;
  655.     vopen();
  656.     vputs("MICRO-Terminal: (Press CTRL-HOME or CTRL-END to exit):\n");
  657.     save_screen();
  658.  
  659. /* If RAM-resident, print startup message & TSR */
  660.     if(argc > 1) {
  661.         draw_title();
  662.         printf("\n\n\nCopyright 1990-1994 Dave Dunfield\nAll rights reserved.\n");
  663.         vcursor_line();
  664.         hot_keys = 0;
  665.         ptr = argv[1];
  666.         while(*ptr) switch(toupper(*ptr++)) {
  667.             case 'A' : hot_keys |= ALT;        break;
  668.             case 'C' : hot_keys |= CONTROL;    break;
  669.             case 'L' : hot_keys |= L_SHIFT;    break;
  670.             case 'R' : hot_keys |= R_SHIFT; break;
  671.             case 'S' : hot_keys |= SYS_REQ;    break;
  672.             default: abort("\nInvalid HOTKEY"); }
  673.         tsr(&tty_main, hot_keys, 2000); }
  674.  
  675. /* Not RAM-resident, execute the program */
  676.     vclscr();        /* Return to blank screen */
  677.     tty_main();
  678. }
  679.